package org.finesys.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.finesys.common.constants.CacheConstants;
import org.finesys.common.core.constants.enums.MenuTypeEnum;
import org.finesys.common.core.exception.ErrorCodes;
import org.finesys.common.core.util.MessageUtil;
import org.finesys.common.mp.core.service.impl.BaseServiceImpl;
import org.finesys.system.api.entity.SysMenu;
import org.finesys.system.api.entity.SysRoleMenu;
import org.finesys.system.mapper.SysMenuMapper;
import org.finesys.system.mapper.SysRoleMenuMapper;
import org.finesys.system.service.SysMenuService;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.finesys.common.constants.BaseConstant.MENU_TREE_ROOT_ID;

@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends BaseServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    private final SysRoleMenuMapper sysRoleMenuMapper;


    @Override
    public Boolean saveMenu(SysMenu sysMenu) {
        return this.save(sysMenu);
    }

    /**
     * 更新菜单
     */
    @Override
    @Caching(evict = {
            @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true),
    })
    public Boolean updateMenu(SysMenu sysMenu) {
        baseMapper.updateById(sysMenu);
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true),
    })
    public Boolean removeMenuById(Long id) {
        //查询父节点为当前节点的的菜单信息
        List<SysMenu> menuList = baseMapper.selectList(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getParentId, id));
        //存在下级节点，不允许删除
        Assert.isTrue(CollUtil.isEmpty(menuList), MessageUtil.getMessage(ErrorCodes.SYS_MENU_DELETE_EXISTING));
        //删除角色关联菜单
        sysRoleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getMenuId, id));
        //删除菜单
        return this.removeById(id);
    }

    @Override
    @Cacheable(value = CacheConstants.MENU_DETAILS, key = "#roleId", unless = "#result == null")
    public Set<SysMenu> findMenuByRoleId(Long roleId) {
        return baseMapper.findMenusByRoleId(roleId);
    }

    /**
     * 构建树查询 1. 不是懒加载情况，查询全部 2. 是懒加载，根据parentId 查询 2.1 父节点为空，则查询ID -1
     *
     * @param parentId 父节点ID
     * @param menuName 菜单名称
     * @return
     */
    public List<Tree<Long>> treeMenu(Long parentId, String menuName, String type) {
        Long parent = parentId == null ? MENU_TREE_ROOT_ID : parentId;
        List<TreeNode<Long>> menuList = baseMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
                        .like(StringUtils.hasText(menuName), SysMenu::getName, menuName)
                        .eq(StringUtils.hasText(type), SysMenu::getMenuType, type)
                        .orderByAsc(SysMenu::getSortOrder)).stream()
                .map(getNodeFunction()).collect(Collectors.toList());
        //模糊查询
        if (StringUtils.hasText(menuName)) {
            return menuList.stream().map(node -> {
                Tree<Long> tree = new Tree<>();
                tree.putAll(node.getExtra());
                BeanUtils.copyProperties(node, tree);
                return tree;
            }).collect(Collectors.toList());
        }
        return TreeUtil.build(menuList, parent);
    }

    /***
     * 查询菜单
     * @param all 全部菜单
     * @param type 类型
     * @param parentId 父节点ID
     * @return
     * */
    public List<Tree<Long>> filterMenu(Set<SysMenu> all, String type, Long parentId) {
        List<TreeNode<Long>> treeList = all.stream().filter(menuTypePredicate(type)).map(getNodeFunction()).collect(Collectors.toList());
        Long parent = parentId == null ? MENU_TREE_ROOT_ID : parentId;
        return TreeUtil.build(treeList, parent);
    }

    @NotNull
    private Function<SysMenu, TreeNode<Long>> getNodeFunction() {
        return menu -> {
            TreeNode<Long> node = new TreeNode<>();
            node.setId(menu.getMenuId());
            node.setName(menu.getName());
            node.setParentId(menu.getParentId());
            node.setWeight(menu.getSortOrder());
            // 扩展属性
            Map<String, Object> extra = new HashMap<>();
            extra.put("path", menu.getPath());
            extra.put("menuType", menu.getMenuType());
            extra.put("permission", menu.getPermission());
            extra.put("sortOrder", menu.getSortOrder());

            // 适配 vue3
            Map<String, Object> meta = new HashMap<>();
            meta.put("title", menu.getName());
            meta.put("isLink", menu.getPath() != null && menu.getPath().startsWith("http") ? menu.getPath() : "");
            meta.put("isHide", !BooleanUtil.toBooleanObject(menu.getVisible()));
            meta.put("isKeepAlive", BooleanUtil.toBooleanObject(menu.getKeepAlive()));
            meta.put("isAffix", false);
            meta.put("isIframe", BooleanUtil.toBooleanObject(menu.getEmbedded()));
            meta.put("icon", menu.getIcon());
            extra.put("meta", meta);
            node.setExtra(extra);
            return node;
        };
    }

    /**
     * menu 类型断言
     *
     * @param type 类型
     * @return Predicate
     */
    private Predicate<SysMenu> menuTypePredicate(String type) {
        return vo -> {
            if (MenuTypeEnum.TOP_MENU.getDescription().equals(type)) {
                return MenuTypeEnum.TOP_MENU.getType().equals(vo.getMenuType());
            }
            // 其他查询 左侧 + 顶部
            return !MenuTypeEnum.BUTTON.getType().equals(vo.getMenuType());
        };
    }

    /**
     * 同步菜单缓存
     */
    @Override
    @CacheEvict(value = CacheConstants.MENU_DETAILS, allEntries = true)
    public void sync() {

    }
}
